
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"

#include "linux/common.h"

int fdt_check_header(void *fdt)
{
    // struct fdt_header *header = fdt;

    if (fdt_magic(fdt) == FDT_MAGIC)
    {
    }

    return 0;
}

void *_fdt_offset_ptr( void *fdt, int offset)
{
    return (char *)fdt + fdt_off_dt_struct(fdt) + offset;
}

/**
 * 获取 FDT 结构块中指定偏移处的指针，并进行安全校验
 *
 * @fdt:     指向 FDT 二进制数据的指针
 * @offset:  相对于 FDT 结构块（dt_struct）的偏移量
 * @len:     需要访问的数据长度
 * 
 * 返回值:   成功时返回指向偏移处的有效指针，失败返回 NULL（表示越界或数据损坏）
 */
void *fdt_offset_ptr( void *fdt, int offset, unsigned int len)
{
    // 计算绝对偏移量：结构块起始位置 + 相对偏移量
    unsigned absoffset = offset + fdt_off_dt_struct(fdt);

    // 全局范围检查：确保访问范围在 FDT 总大小内 
    // 检查整数溢出和越界：
    // - 若 offset 为负，absoffset < offset 可能为真（无符号与有符号比较）
    // - absoffset + len 是否溢出或超出 FDT 总大小
    if ((absoffset < offset) || ((absoffset + len) < absoffset) || (absoffset + len) > fdt_totalsize(fdt))
        return NULL;

    //版本 >= 0x11 的额外检查：确保在结构块内部范围
    // 高版本 FDT 要求结构块内的严格边界检查
    if (fdt_version(fdt) >= 0x11)
        if (((offset + len) < offset) || ((offset + len) > fdt_size_dt_struct(fdt)))   // 检查 offset + len 是否溢出或超出结构块大小
            return NULL;

    return _fdt_offset_ptr(fdt, offset); //返回实际指针（基于结构块起始地址 + 偏移量） 
}

/**
 * 功能：   
 *      从 FDT 的指定偏移处解析下一个标签类型，并更新下一个标签的偏移量
 * 参数：
 *  fdt:        指向 FDT 二进制数据的指针
 *  startoffset: 起始偏移量（从 FDT 头部开始计算）
 *  nextoffset: 输出参数，用于返回下一个标签的偏移量
 * 
 * 返回值:      
 *      当前标签的类型（如 FDT_BEGIN_NODE、FDT_PROP 等），
 *       如果解析失败则返回 FDT_END
 */

uint32_t fdt_next_tag( void *fdt, int startoffset, int *nextoffset)
{
    fdt32_t *tagp, *lenp;  // 标签指针和属性长度指针
    uint32_t tag; // 当前标签类型
    int offset = startoffset;   // 当前偏移量
    char *p; // 临时指针（用于遍历字符串）

    *nextoffset = -FDT_ERR_TRUNCATED;  // 初始化为截断错误，若后续未正确更新，表示 FDT 数据不完整
    tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);  // 从当前偏移处读取标签（标签占 4 字节）
    if (!tagp)
        return FDT_END; // FDT 数据不完整，提前结束
    tag = fdt32_to_cpu(*tagp); // 将标签从大端转换为 CPU 字节序
    offset += FDT_TAGSIZE; // 偏移量前进 4 字节（跳过标签）

    *nextoffset = -FDT_ERR_BADSTRUCTURE; // 初始化为结构错误，若后续未正确更新，表示 FDT 结构错误
    switch (tag)
    {
    case FDT_BEGIN_NODE:  // 节点开始标签（如 `/node_name`）
        /* skip name */  // 跳过节点名称（以 '\0' 结尾的字符串）
        do
        {
            p = fdt_offset_ptr(fdt, offset++, 1); // 逐字节读取字符
        } while (p && (*p != '\0')); // 直到遇到字符串结束符
        if (!p)
            return FDT_END; // 数据不完整，无法找到结束符
        break;

    case FDT_PROP: // 属性标签（如 `reg = <...>`)
        lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));  // 获取属性长度字段（占 4 字节）
        if (!lenp)
            return FDT_END; // 数据不完整，无法读取属性长度
        
        // 跳过以下内容：
        // - 属性名的偏移（4 字节，但已包含在结构体中）
        // - 属性长度（4 字节）
        // - 属性值
        offset += sizeof(struct fdt_property) - FDT_TAGSIZE + fdt32_to_cpu(*lenp);
        break;

    case FDT_END:  // FDT 结束标签（整个设备树结束）
    case FDT_END_NODE: // 节点结束标签（如 `};`）
    case FDT_NOP: // 空操作标签（占位符）
        break;

    default: // 未知标签类型
        return FDT_END;// 非法标签，终止生产，啊不终止解析
    }

    //验证当前处理的范围是否在有效数据内
    if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
        return FDT_END; // 处理过程中超出 FDT 数据范围

    *nextoffset = FDT_TAGALIGN(offset); //计算下一个标签的对齐偏移量（按 4 字节对齐）
    return tag; // 返回当前标签类型
}

/*
 *  参数：
 *      void *fdt
 *      指向设备树二进制数据（DTB）的指针(设备树存放地址)，用于访问整个设备树结构。
 *
 *      int offset
 *      当前遍历的起始偏移量。若为 -1，表示从根节点开始遍历；若为有效偏移量，则从该位置继续查找下一个节点。
 *
 *      int *depth
 *      指向当前节点深度的指针。用于跟踪节点的层级关系（如根节点深度为 0，子节点递增）。
 *      可为 NULL，表示不需要跟踪深度。
 *
 *  返回值：
 *      成功时：返回下一个节点（FDT_BEGIN_NODE）的偏移量（int 类型）。
 *      错误时：返回负的错误代码（如 -FDT_ERR_BADOFFSET 表示无效偏移）。
 *      遍历完成：返回 -FDT_ERR_NOTFOUND，表示没有更多节点。
 *
 *  功能：
 *      遍历设备树中的节点，处理节点的开始（FDT_BEGIN_NODE）和结束（FDT_END_NODE），并更新深度信息。
 *      跳过属性（FDT_PROP）和空操作（FDT_NOP）标签，仅关注节点结构。
 *      支持递归遍历子节点，通过 depth 参数维护节点的层次关系。
 */

int fdt_next_node( void *fdt, int offset, int *depth)
{
    int nextoffset = 0; // 下一个标签的偏移量
    uint32_t tag; // 存储当前解析的标签类型（如 FDT_BEGIN_NODE）

    /* 检查当前偏移是否有效（offset >= 0 表示从某个节点开始遍历） */
    if (offset >= 0)
        if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)  // 调用 _fdt_check_node_offset 验证当前偏移是否指向有效节点, 若无效，返回错误码（如 -FDT_ERR_BADOFFSET）
            return nextoffset;

    /*主循环：遍历标签直到找到下一个节点 */
    do
    {
        // 更新当前偏移量为下一个标签的位置
        offset = nextoffset;
        tag = fdt_next_tag(fdt, offset, &nextoffset); // 调用 fdt_next_tag 解析当前标签，并获取下一个标签的偏移量 nextoffset

        switch (tag)
        {
        case FDT_PROP:  // 属性标签（如 `reg`、`compatible`）
        case FDT_NOP:  // 空操作标签，跳过
            break;  // 直接继续循环，不处理

        case FDT_BEGIN_NODE: // 节点开始标签：更新深度 
            if (depth)
                (*depth)++; // 增加当前深度（进入子节点）
            break;

        case FDT_END_NODE:  // 节点结束标签：减少深度
            if (depth && ((--(*depth)) < 0)) // 若深度减至负数，说明已遍历完根节点的所有子节点
                return nextoffset;  // // 返回当前偏移量（结束遍历）
            break;

        case FDT_END: // 设备树结束标签：处理终止条件
            // 若 nextoffset >= 0，表示存在未闭合的节点，返回未找到错误
            // 若 nextoffset == -FDT_ERR_TRUNCATED 且无深度跟踪，返回未找到错误
            if ((nextoffset >= 0) || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
                return -FDT_ERR_NOTFOUND;
            else
                return nextoffset;
                //正常设备树：
                //FDT_END 是最后一个标签，fdt_next_tag 应设置 nextoffset 为超出设备树大小的值。此时 fdt_offset_ptr 检查失败，nextoffset 可能被设为 -FDT_ERR_BADOFFSET 或其他错误码。
                //损坏的设备树：
                //若 FDT_END 出现在未闭合的节点内，nextoffset 可能为有效偏移量（正数），触发 nextoffset >= 0 条件，返回 -FDT_ERR_NOTFOUND
        }
    } while (tag != FDT_BEGIN_NODE); // 循环直到找到下一个节点开始

    return offset;  //  返回当前节点起始偏移（FDT_BEGIN_NODE 的位置）
}